package com.service.serviceImpl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.apiresult.ResultCode;
import com.common.constant.RedisConstant;
import com.common.domain.dto.AddUserDTO;
import com.common.domain.dto.SendEmailDTO;
import com.common.domain.dto.UserUpdateInfoDTO;
import com.common.exception.BusinessException;
import com.common.utils.JwtTokenUtil;
import com.common.utils.RedisUtil;
import com.common.utils.StringUtil;
import com.service.serviceInterface.UserService;
import com.system.entity.*;
import com.system.mapper.*;
import io.lettuce.core.RedisException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.Arrays;

/**
 *
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private StudentInfoMapper studentInfoMapper;
    @Resource
    private WorkInfoMapper workInfoMapper;
    @Resource
    private GuestInfoMapper guestInfoMapper;
    @Resource
    private StringUtil stringUtil;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    @Resource
    private RestTemplate restTemplate;
    @Value(value = "${sendEmailCode.url}")
    private String sendEmailUrl;

    /**
     * 注册用户
     * 邮箱号不能重复
     * 默认赋予 访客身份
     *
     * @param addUserDTO
     */
    @Override
    @Transactional(rollbackFor = BusinessException.class, propagation = Propagation.REQUIRED)
    public void register(AddUserDTO addUserDTO) {
        //查询邮箱号是否已经存在
        Integer count = userMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getEmail, addUserDTO.getEmail()));
        if (count > 0) {
            log.info("已存在此邮箱号 {} ", addUserDTO.getEmail());
            throw new BusinessException(ResultCode.EMAILALREADYEXIST.getMessage() + addUserDTO.getEmail());
        }
        //添加用户，添加用户角色
        long userId = IdWorker.getId();
        addUserDTO.setId(userId);
        boolean addUser = addUser(addUserDTO);
        boolean addUserRole = addUserRole(userId);
        if (addUser == false || addUserRole == false) {
            throw new BusinessException(ResultCode.FAILED);
        }
    }

    @Override
    public String login(String username, String password) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authenticate = null;
        try {
            authenticate = authenticationManager.authenticate(token);
        } catch (AuthenticationException e) {
            throw new BusinessException(ResultCode.USERNAMEPASSWORDERROR);
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        redisUtil.set(RedisConstant.LOGIN_USER_PREFIX + loginUser.getUser().getId(), loginUser, RedisConstant.LOGIN_USER_TIME);
        return jwtTokenUtil.createToken(loginUser.getUser().getId(), loginUser.getUsername());
    }

    @Override
    @Transactional(rollbackFor = {RedisException.class, SQLException.class, BusinessException.class, Exception.class, Error.class})
    public void updatePwd(String email, String password) {
        String encodePwd = bCryptPasswordEncoder.encode(password);
        int i = userMapper.update(null, new LambdaUpdateWrapper<User>().set(User::getPassword, encodePwd).eq(User::getEmail, email));
        if (i != 1) {
            throw new BusinessException(ResultCode.FAILED);
        }
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        loginUser.getUser().setPassword(encodePwd);
        long expire = redisUtil.getExpire(RedisConstant.LOGIN_USER_PREFIX + loginUser.getUser().getId());
        redisUtil.set(RedisConstant.LOGIN_USER_PREFIX + loginUser.getUser().getId(), loginUser, expire);
    }

    @Override
    public void sendEmail(String subject, String context, String to) {
        SendEmailDTO dto = new SendEmailDTO(subject, context, to);
        restTemplate.postForObject(sendEmailUrl, dto, String.class);
    }

    @Override
    public String getUserNameById(Long modifyUserId) {
        return userMapper.getUserNameById(modifyUserId);
    }

    @Override
    @Transactional(rollbackFor = BusinessException.class, propagation = Propagation.REQUIRED)
    public void updateInfo(UserUpdateInfoDTO dto, LoginUser loginUser) {
        boolean flag = true;
        if (!loginUser.getUser().getEmail().equals(dto.getEmail())) {
            int update = userMapper.update(null, new LambdaUpdateWrapper<User>().set(User::getEmail, dto.getEmail()));
            if (update != 1) flag = false;
        }
        if (loginUser.getRoles().contains("student")) {
            int update = studentInfoMapper.update(null, new LambdaUpdateWrapper<StudentInfo>().set(StudentInfo::getName, dto.getNickName())
                    .set(StudentInfo::getPhone, dto.getPhone()).set(StudentInfo::getSex, dto.getSex()).eq(StudentInfo::getUserId, loginUser.getUser().getId()));
            if (update != 1) flag = false;
        } else if (loginUser.getRoles().stream().anyMatch(i -> Arrays.asList("dormManagement", "plumber", "schoolManagement").contains(i))) {
            int update = workInfoMapper.update(null, new LambdaUpdateWrapper<WorkInfo>().set(WorkInfo::getName, dto.getNickName()).set(WorkInfo::getPhone, dto.getPhone()).set(WorkInfo::getSex, dto.getSex()).set(WorkInfo::getModifyTime, LocalDateTime.now()).set(WorkInfo::getModifyUserId, loginUser.getUser().getId()).eq(WorkInfo::getUserId, loginUser.getUser().getId()));
            if (update != 1) flag = false;
        } else if (loginUser.getRoles().contains("guest")) {
            int update = guestInfoMapper.update(null, new LambdaUpdateWrapper<GuestInfo>().set(GuestInfo::getName, dto.getNickName()).set(GuestInfo::getPhone, dto.getPhone()).set(GuestInfo::getSex, dto.getSex()).set(GuestInfo::getModifyTime, LocalDateTime.now()).set(GuestInfo::getModifyUserId, loginUser.getUser().getId()).eq(GuestInfo::getUserId, loginUser.getUser().getId()));
            if (update != 1) flag = false;
        }
        if (flag == false) {
            throw new BusinessException(ResultCode.FAILED);
        }
    }

    /**
     * 添加用户
     *
     * @param addUserDTO
     * @return
     */
    public boolean addUser(AddUserDTO addUserDTO) {
        User user = new User();
        BeanUtils.copyProperties(addUserDTO, user);
        user.setRegister(LocalDateTime.now());
        user.setUsername(stringUtil.getUserName(10));
        GuestInfo guestInfo = new GuestInfo();
        guestInfo.setUserId(addUserDTO.getId()).setAddTime(LocalDateTime.now()).setAddUserId(addUserDTO.getId());
        return userMapper.insert(user) == 1 && guestInfoMapper.insert(guestInfo) == 1;
    }

    /**
     * 赋予用户默认访客身份
     *
     * @return
     */
    public boolean addUserRole(long val) {
        UserRole userRole = new UserRole();
        userRole.setAddTime(LocalDateTime.now());
        userRole.setAddUserId(val);
        userRole.setStatus(1);
        userRole.setRoleId((long) 5);
        userRole.setUserId(val);
        return userRoleMapper.insert(userRole) == 1;
    }
}




